package org.example.demo.config;

import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWTPayload;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.demo.cache.LoginUserCache;
import org.example.demo.common.GlobalSettings;
import org.example.demo.entity.dto.Result;
import org.example.demo.utils.JwtUtil;
import org.example.demo.utils.RequestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Configuration
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {

    @Value("${app.jwt-secret-key}")
    private String jwtSecretKey;

    @Value("#{'${app.no-login-authentication-url:}'.empty ? null : '${app.no-login-authentication-url:}'.split(',')}")
    private List<String> noLoginAuthenticationUrls;

    @Value("#{'${app.no-authorization-authentication-url:}'.empty ? null : '${app.no-authorization-authentication-url:}'.split(',')}")
    private List<String> noAuthorizationAuthenticationUrls;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private LoginUserCache loginUserCache;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

                loginUserCache.set(null);

                String uri = request.getRequestURI();
                String contextPath = request.getContextPath();
                log.debug("[Context Path] ======>> {} | [Request URI] ======>> {}", contextPath, uri);

                for (String url : noLoginAuthenticationUrls) {
                    if (uri.equals(contextPath + url)) {
                        return true;
                    }
                }

                String token = RequestUtil.getToken(request);
                JSONObject payloads = JwtUtil.getJwtPayloads(jwtSecretKey, token);
                log.debug("[Token Payloads] {}", payloads);
                if (payloads == null) {
                    onNoLoginError(response);
                    return false;
                }

                String jwtId = (String) payloads.get(JWTPayload.JWT_ID);
                if (redisTemplate.hasKey(GlobalSettings.LOGIN_USER_BLACK_LIST_PREFIX + jwtId)) {
                    onNoLoginError(response);
                    return false;
                }

                String username = (String) payloads.get(GlobalSettings.LOGIN_USER);
                loginUserCache.set(username);

                for (String url : noAuthorizationAuthenticationUrls) {
                    if (uri.equals(contextPath + url)) {
                        return true;
                    }
                }

                // TODO: 2024/3/4 用户访问权限在此判断
                return true;


            }
        }).addPathPatterns("/**").excludePathPatterns("/favicon.ico", "/doc.html", "/swagger-resources", "/webjars/**", "/assets/**", "/datasource/**");
    }

    private void onNoLoginError(HttpServletResponse response) {
        Result result = Result.error("用户未登录或者登录状态已失效，请重新登录");
        response.setHeader("Content-Type", "application/json;charset=UTF-8"); // 指定编码，否则在浏览器中会中文乱码
        try {
            response.getWriter().write(JSON.toJSONString(result));
        } catch (IOException e) {
            log.error("系统错误：", e);
        }
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    }
}
